/* eslint-disable @typescript-eslint/no-explicit-any */
import { sys } from 'cc';
import { ItemList, LimitType, Tables } from '../../Data/Types';
import { StringUtil } from '../../Util/StringUtil';
import { DataTempParams, GameParams, PlayerParams } from '../Config/DataConfig';
import { _classExample, Singleton } from './BaseManager';
import { MathUtil } from '../../Util/MathUtil';
import { GravityModel } from '../Model/SDK/GravityModel';

/** 数据管理器 */
class DataManager extends Singleton {
  className: string = 'DataManager';
  static gameKey: string = 'SlidingElimination';
  /** 表格数据 */
  tables: Tables = null!;
  /** 临时数据 */
  tempSaveData: Map<DataTempParams, any> = new Map();

  /** 玩家属性数据 */
  playerParams: Map<PlayerParams, any> = new Map<PlayerParams, any>([
    [PlayerParams.isFirst, true],
    [PlayerParams.isNovice, true],
    [PlayerParams.firstLoadTime, 0],
    [PlayerParams.adCount, 0],
    [PlayerParams.liveDay, 0],
    [PlayerParams.maxStage, 0],
    [PlayerParams.registDays, 0],
    [PlayerParams.nickname, '游客'],
    [PlayerParams.avatarUrl, ''],
    [PlayerParams.level, 1],
    [PlayerParams.loginTime, 0],
    [PlayerParams.loginDay, 0],
    [PlayerParams.myRankData, []],
    [PlayerParams.rankData, []],
    [PlayerParams.open_id, sys.isBrowser ? MathUtil.generateUUID() : ''],
    [PlayerParams.session_key, MathUtil.generateUUID()],
  ]);

  gameParams: Map<GameParams, any> = new Map<GameParams, any>([
    [GameParams.gameMusic, true],
    [GameParams.gameSound, true],
    [GameParams.gameShark, true],
    [GameParams.gameSpeed, 1],
    [GameParams.gameLevel, 1],
    [GameParams.gameDebug, false],
    [GameParams.item_box, 0],
    [GameParams.item_scoop1, 0],
    [GameParams.item_scoop2, 0],
    [GameParams.sidebar, 0],
    [GameParams.gameChallenge, {}],
    // [GameParams.prop_4, 0],
    // [GameParams.prop_5, 0],
    // [GameParams.prop_6, 0],
    [GameParams.level_visitor, 1],
    [GameParams.coin, 0],
    [GameParams.energy, 0],
    [GameParams.energy_award, 0],
  ]);

  init() {
    const data = sys.localStorage;
    const playerDataKey = `PlayerParams_${DataManager.gameKey}`;
    const gameDataKey = `GameParams_${DataManager.gameKey}`;
    const playerData = data.getItem(playerDataKey);
    const gameData = data.getItem(gameDataKey);

    if (playerData) {
      const param = JSON.parse(playerData);
      for (const key in param) {
        this.playerParams.set(key as PlayerParams, param[key]);
      }
    } else {
      Data.saveData(playerDataKey, this.playerParams);
    }

    if (gameData) {
      const param = JSON.parse(gameData);
      for (const key in param) {
        this.gameParams.set(key as GameParams, param[key]);
      }
    } else {
      Data.saveData(gameDataKey, this.gameParams);
    }
    this.SaveData();
    // RedDotManager.Ins().initRedDotData();
  }

  /** 检测新的一天 */
  checkNewDay() {
    const date = new Date();
    const lastTime = new Date(this.playerParams.get(PlayerParams.loginTime)).setHours(0, 0, 0, 0);
    const nowTime = date.setHours(0, 0, 0, 0);
    if (nowTime - lastTime >= 86400000) {
      // 新的一天
      // 参数重置
      this.changePlayerAttr(PlayerParams.loginDay, 1);
      this.changePlayerAttr(PlayerParams.liveDay, 1);

      // GravityModel.I.userSet({ acitveDays: PlayerData.get(PlayerParams.liveDay) });
      const firstTime = new Date(PlayerData.get(PlayerParams.firstLoadTime)).setHours(0, 0, 0, 0);
      const day = Math.ceil((nowTime - firstTime) / 86400000);
      this.changePlayerAttr(PlayerParams.registDays, day, true);
      // GravityModel.I.userSet({ registDays: PlayerData.get(PlayerParams.registDays) });
    }
    this.changePlayerAttr(PlayerParams.loginTime, nowTime, true);
  }

  /** 改变玩家属性 */
  changePlayerAttr(attrId: PlayerParams, value: number | string | any, reset: boolean = false) {
    let temp;
    if (typeof value == 'number') {
      if (reset || !this.playerParams.has(attrId)) {
        this.playerParams.set(attrId, value);
      } else {
        temp = this.playerParams.get(attrId) + value;
        this.playerParams.set(attrId, temp);
      }
    } else {
      temp = value;
      this.playerParams.set(attrId, value);
    }
    this.SaveData('player');
  }

  /** 改变玩家属性 */
  changeGameAttr(attrId: GameParams, value: any, reset: boolean = false) {
    let temp;
    if (typeof value == 'number') {
      if (reset || !this.gameParams.has(attrId)) {
        this.gameParams.set(attrId, value);
      } else {
        temp = this.gameParams.get(attrId) + value;
        this.gameParams.set(attrId, temp);
      }
    } else if (typeof value === 'string') {
      temp = value;
      this.gameParams.set(attrId, value);
    } else {
      this.gameParams.set(attrId, value);
    }
    this.SaveData('game');
  }

  SaveData(key: string = '') {
    const playerDataKey = `PlayerParams_${DataManager.gameKey}`;
    const gameDataKey = `GameParams_${DataManager.gameKey}`;
    if (key === '') {
      Data.saveData(playerDataKey, this.playerParams);
      Data.saveData(gameDataKey, this.gameParams);
    } else if (key === 'player') {
      Data.saveData(playerDataKey, this.playerParams);
    } else {
      Data.saveData(gameDataKey, this.gameParams);
    }
  }
}

class DataProxyHandler {
  /**
   *
   * @param target 被代理的对象
   * @param prop 被代理的对象的属性名
   * @param value 被代理的对象的属性值
   * @param receiver 代理对象
   * @returns
   */
  set(target: object, prop: string | symbol, value: any, receiver: any): boolean {
    const oldValue = Reflect.get(target, prop, receiver);
    if (Data.hasChanged(value, oldValue)) {
      if (Data.isObject(value)) {
        sys.localStorage(target.constructor.name + '_' + String(prop), JSON.stringify(value));
      } else {
        sys.localStorage(target.constructor.name + '_' + String(prop), value);
      }
      Reflect.set(target, prop, value, receiver);
      return true;
    }
    return false;
  }

  deleteProperty(target: object, prop: string | symbol): boolean {
    return Reflect.deleteProperty(target, prop);
  }
}

// eslint-disable-next-line @typescript-eslint/no-namespace, @typescript-eslint/prefer-namespace-keyword
export module Data {
  /** 获取奖励数据 */
  export function getRewardData(list: ItemList | ItemList[], ...args: any[]): [type: number, id: number, num: number][] {
    let goods;
    if (Array.isArray(list)) {
      goods = list;
    } else {
      goods = [list];
    }
    const result = [];
    for (let i = 0; i < goods.length; i++) {
      const good: ItemList = goods[i];
      if (!Data.checkLimit(good.type, good.limit, ...args)) continue;
      result.push(...good.items);
    }
    return result;
  }

  /** 判断范围 */
  export function checkLimit(limitType: LimitType, limit: string, ...args: any[]): boolean {
    switch (limitType) {
      case LimitType.LIMIT_SPACE:
        return Number(limit) == args[0];
      case LimitType.LIMIT_SPACE_CONTRARY:
        return Number(limit) != args[0];
      case LimitType.LIMIT_SPACE_ITEM: {
        const ll = limit.split('_');
        return args[0] >= Number(ll[0]) && args[0] <= Number(ll[1]);
      }
      case LimitType.LIMIT_SPACE_ITEM_CONTRARY: {
        const ll = limit.split('_');
        return args[0] < Number(ll[0]) || args[0] > Number(ll[1]);
      }
      case LimitType.LIMIT_SPACE_LIST: {
        const ll = StringUtil.splitStrToNum(limit, ',_');
        for (let i = 0; i < args.length; i++) {
          let boo = false;
          for (let j = 0; j < ll.length; j++) {
            if (args[i] >= ll[j][0] && args[i] <= ll[j][1]) {
              boo = true;
              break;
            }
          }
          if (!boo) return false;
        }
        return true;
      }
      case LimitType.LIMIT_SPACE_LIST_CONTRARY: {
        const ll = StringUtil.splitStrToNum(limit, ',_');
        for (let i = 0; i < args.length; i++) {
          let boo = false;
          for (let j = 0; j < ll.length; j++) {
            if (args[i] >= ll[j][0] && args[i] <= ll[j][1]) {
              boo = true;
              break;
            }
          }
          if (boo) return false;
        }
        return true;
      }
      default:
        return false;
    }
  }

  export function isObject(value: unknown): value is Record<any, any> {
    return value !== null && typeof value === 'object';
  }

  export const hasOwnProperty = Object.prototype.hasOwnProperty;

  /** 比较值是否变化了，考虑NaN；如果是对象直接认为改变了 */
  export function hasChanged(value: any, oldValue: any): boolean {
    if (typeof value === 'object') return true;
    return !Object.is(value, oldValue);
  }

  export function newDataProxy<T>(target: _classExample<T>, ...args): T {
    const data = new target(...args);
    return new Proxy(data as object, new DataProxyHandler()) as T;
  }

  export function registerDataProxy<T>(target: T): T {
    return new Proxy(target as object, new DataProxyHandler()) as T;
  }

  export function saveData(str: string, value: any) {
    if (typeof value == 'object') {
      const result = Object.fromEntries(value);
      sys.localStorage.setItem(str, JSON.stringify(result));
    } else {
      sys.localStorage.setItem(str, JSON.stringify(value));
    }
  }

  export function copyData<T>(target: T, data: T, ignore?: string[]): T {
    for (const key in data) {
      if (ignore && ignore.includes(key)) continue;
      if (target[key] instanceof Map) {
        for (const k in data[key]) {
          target[key].set(k, data[key][k]);
        }
      } else {
        target[key] = data[key];
      }
    }
    return target;
  }
}

export const DataMgr = DataManager.Ins();
export const TempSaveData = DataMgr.tempSaveData;
export const PlayerData = DataMgr.playerParams;
export const GameData = DataMgr.gameParams;
